/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/
/*
Authors: Ram Ovadia 201374022 and Lior Fass 300069705
Project: HW Assignment 3
Description: This is the header of the BaggageTracker main utils module used in the project.  
*/
/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#include "BaggageTrackerUtils.h"

BOOL isInteger(char *str)
{
	int i, length = 0;

	length = strlen(str);
	for (i=0; i < length; i++)
	{
		if ( !( (str[i] > 47) && (str[i] < 58) ) )
		{
			return FALSE;
		}
	}
	return TRUE;
}


tArgs *init(int num_of_slots, int num_of_sorters, FILE* in_f)
{
	int i;
	tArgs *common;

	common = (tArgs *)malloc(sizeof(tArgs));
	if (common == NULL)
	{
		printf("memory allocation failure common args\r\n");
		return NULL;
	}

	// ALLOCATE CREATE ARRAY OF SLOTS
	common->slots = (tSlot *)calloc(num_of_slots, sizeof(tSlot) * SLOT_LEN);
	if (common->slots == NULL)
	{
		printf("memory allocation failure common->slots\r\n");
		return NULL;
	}

	// ALLOCATE CREATE ARRAY OF MUTESXES FOR THE SLOTS 
	common->slots_mutexes = (HANDLE *)malloc(num_of_slots * sizeof(HANDLE));
	if (common->slots_mutexes == NULL)
	{
		printf("memory allocation failure common->slots_mutexes\r\n");
		return NULL;
	}
	for (i=0; i<num_of_slots; i++)
	{
		common->slots_mutexes[i] = CreateMutex(NULL, FALSE, NULL);
		if (common->slots_mutexes[i] == NULL)
		{
			printf("memory allocation failure common->slots_mutexes[i]\r\n");
			return NULL;
		}
	}

	// ALLOCATE NEW LIST FOR LIST OF PLANES.
	common->planes = List_create();
	if (common->planes == NULL)
	{
		printf("memory allocation failure common->planes\r\n");
		return NULL;
	}

	// ALLOCATE WRITE PROTECTION MUTEX (LAST LINK OF LIST)
	common->planes_push_mutex = CreateMutex(NULL, FALSE, NULL);
	if (common->planes_push_mutex == NULL)
	{
		printf("memory allocation failure common->planes_push_mutex\r\n");
		return NULL;
	}

	// ALLOCATE SYNC SEMPAHORE, INITIATED TO ZERO.
	common->sync_sempahore = CreateSemaphore(NULL, 0, num_of_sorters+NUM_OF_LOADERS, NULL); 
	if (common->sync_sempahore == NULL)
	{
		printf("memory allocation failure commmon->sync_semaphore\r\n");
		return NULL;
	};

	common->in_f = in_f;
	common->num_of_slots = num_of_slots;

	return common;
}

int dispatcher(tArgs *common, int num_of_sorters)
{
	int i;
	HANDLE *sorter_thread_handlers = NULL;

	// RUN LOADING THREAD(*ARG_STRCUT)
	common->loader_thread_handle = RunThread((int (*)(LPVOID *))loader_thread, (void *)common);
	if (common->loader_thread_handle == NULL)
	{
		printf("failed to create thread loader_thread\n");
		return 1;
	}

	// RUN SORTER THREADS(*ARG_STRCUT) 
	sorter_thread_handlers = (HANDLE *)malloc(num_of_sorters*sizeof(HANDLE));
	if (sorter_thread_handlers == NULL)
	{
		printf("memory allocation failure sorter_thread_handlers\r\n");
		return 1;
	}

	for (i=0; i<num_of_sorters; i++)
	{
		sorter_thread_handlers[i] = RunThread((int (*)(LPVOID *))sorter_thread, (void *)common);
		if (sorter_thread_handlers[i] == NULL)
		{
			printf("failed to create thread sorter\n");
			return 1;
		}
	}

	// release sync semaphore
	ReleaseSemaphore(common->sync_sempahore, num_of_sorters+NUM_OF_LOADERS, NULL);

	// WAIT ON MULTIPLE OBJECTS
	WaitForMultipleObjects(num_of_sorters, sorter_thread_handlers, TRUE, 100000);

	for (i=0; i<num_of_sorters; i++)
	{
		CloseHandle(sorter_thread_handlers[i]);
	}

	List_insert_sort(common->planes, comp_planes);
	return 0;
}

int input_check(int argc, char *argv[], int *num_of_slots, int *num_of_sorters, FILE **in_f, FILE **out_f)
{
	// test for input arguments
	if (argc != 5)
	{
		printf("Wrong numbet of arguments\r\n");
		return 1;
	}

	//test inout file exists
	*in_f = fopen(argv[IN_TXT_FILE_ARG],"r");
	*out_f = fopen(argv[OUT_TXT_FILE_ARG],"w");
	
	if (*in_f == NULL)
	{
		printf("Cannot open input file as specified: %s\r\n",argv[IN_TXT_FILE_ARG]);
		return 1;
	}

	//test output file can be opened 
	if (*out_f == NULL)
	{
		printf("Cannot open output file as specified: %s\r\n",argv[OUT_TXT_FILE_ARG]);
		return 1;
	}

	//test valid positive number of slots
	if ( (!isInteger(argv[NUM_OF_SLOTS_ARG])) || (1 > (*num_of_slots = atoi(argv[NUM_OF_SLOTS_ARG]))) )
	{
		printf ("invlaid or zero number of slots specified: %s\r\n", argv[NUM_OF_SLOTS_ARG]);
		return 1;
	}

	//test valid positive number of slots
	if ( (!isInteger(argv[NUM_OF_SORTERS_ARG])) || (1 > (*num_of_sorters = atoi(argv[NUM_OF_SORTERS_ARG]))) )
	{
		printf ("invlaid or zero number of slots specified: %s\r\n", argv[NUM_OF_SORTERS_ARG]);
		return 1;
	}

	return 0;
}

int luggage_count(List *planes)
{
	int sum = 0;
	LIST_FOREACH(planes, first, next, curr){
		sum += ((tPlane *)(curr->value))->luggage_count;
	}
	return sum;
}

int output_generate(tArgs *common, FILE *out_f)
{
	//sort list of planes alphabetically according to dest.  
	LIST_FOREACH(common->planes, first, next, curr){
		fprintf(out_f, "%d pieces of baggage were sent to %.3s.\n", ((tPlane *)(curr->value))->luggage_count, ((tPlane *)(curr->value))->dest);
	}
	fprintf(out_f, "In total, %d pieces of baggage were handled.", luggage_count(common->planes));
	return 0;
}

HANDLE RunThread(  int (*func)( LPVOID* ) , LPVOID ArgPtr )
{

	LPTHREAD_START_ROUTINE startFunction = ( LPTHREAD_START_ROUTINE ) func;
	return CreateThread(
		NULL,            /*  default security attributes */
		0,               /*  use default stack size */
		startFunction,   /*  thread function */
		ArgPtr,    /*  argument to thread function */
		0,               /*  use default creation flags */
		NULL );			 /*  we don't care right now about thread identifier */
}

void print_slot(void *slot)
{
	printf("%.3s", (tSlot*)slot);
}

void print_plane(void *plane)
{
	printf("Plane to: %.3s, has %d pieces of luggage", ((tPlane *)plane)->dest, ((tPlane *)plane)->luggage_count);
}

int comp_planes(void *planeA, void *planeB)
{
	return strncmp(((tPlane *)planeA)->dest, ((tPlane *)planeB)->dest, SLOT_LEN);
}

void free_plane(void *plane)
{
	if (plane != NULL)
	{
		free(((tPlane *)plane)->dest);
		if (((tPlane *)plane)->luggage_mutex != NULL)
		{
			CloseHandle(((tPlane *)plane)->luggage_mutex);
		}
		free(plane);
	}
}

int finalize_run(tArgs *common, FILE *out_f, FILE *in_f)
{
	int i;
	if (out_f!=NULL)
	{
		fclose(out_f);
	}
	if (in_f!=NULL)
	{
		fclose(in_f);
	}
	if (common!=NULL)
	{
		if (common->loader_thread_handle != NULL)
		{
			CloseHandle(common->loader_thread_handle);
		}
		if (common->planes_push_mutex != NULL)
		{
			CloseHandle(common->planes_push_mutex);
		}
		if (common->sync_sempahore != NULL)
		{
			CloseHandle(common->sync_sempahore);
		}

		for (i=0; i<common->num_of_slots; i++)
		{
			CloseHandle(common->slots_mutexes[i]);
		}

		if (common->planes != NULL)
		{
			List_destroy(common->planes, free_plane);
		}
		free(common->slots);
		free(common);
	}
	return 0;
}